/*!
 *  \file       DeviceEntropyGenerator.cpp
 *  \brief      Implementazione della classe \c DeviceEntropyGenerator
 *  \author     Davide Scola <S2299453@studenti.unige.it>
 *  \author	Minetti Alberto <S3064777@studenti.unige.it>
 *  \version    1.0
 */

#include <ctime>
#include <string>
#include <limits>
#include <cstdlib>
#include <fstream>
#include "IEntropyGenerator.hpp"
#include "TimeEntropyGenerator.hpp"
#include "DeviceEntropyGenerator.hpp"

using namespace std;


/*!
 *  \brief      Controlla la sorgente di entropia
 */
inline void
DeviceEntropyGenerator::resetSource( void )
{
  this->source_->close();
  
  delete this->source_;
  this->source_ = 0;
}


/*!
 *  \brief      Costruttore di \c DeviceEntropyGenerator
 */
DeviceEntropyGenerator::DeviceEntropyGenerator( std::string deviceName )
    : TimeEntropyGenerator( )
{
  this->source_ = new fstream( deviceName.c_str( ), std::ios_base::binary | std::ios_base::in );
  
  if( 0 != this->source_ && !this->source_->good( ) )
    this->resetSource( );
}

/*!
 *  \brief      Costruttore di copia di \c DeviceEntropyGenerator
 *  \param[in]	other	generatore da copiare
 */
DeviceEntropyGenerator::DeviceEntropyGenerator( DeviceEntropyGenerator const& other )
    : TimeEntropyGenerator( static_cast< TimeEntropyGenerator >( other ) )
{  
  this->source_ = (&other)->source_;
}


/*!
 *  \brief      Distruttore di \c DeviceEntropyGenerator
 */
DeviceEntropyGenerator::~DeviceEntropyGenerator( )
{
  if( 0 != this->source_ )
    this->resetSource( );
}


/*!
 *  \brief      Operatore di assegnazione della classe \c DeviceEntropyGenerator
 *
 */
DeviceEntropyGenerator &
DeviceEntropyGenerator::operator=( const DeviceEntropyGenerator& other )
{
   if (this == &other) {
       return *this;
   }
   
   this->seed_ = other.seed_;
   srandom( this->seed_ );
   
   this->source_ = (&other)->source_;
   return *this;
}


/*!
 *  \brief      Operatore di uguaglianza della classe \c DeviceEntropyGenerator
 */
bool
DeviceEntropyGenerator::operator==( const DeviceEntropyGenerator& other ) const
{
    return this->seed_ == (&other)->seed_	&&
	    this->source_ == (&other)->source_;
}

/*!
 *  \brief      Operatore di diseguaglianza della classe \c DeviceEntropyGenerator
 */
bool
DeviceEntropyGenerator::operator!=( const DeviceEntropyGenerator& other ) const
{
    return !( *this == other );
}


/*!
 *  \brief      Ritorna il valore pseudo-random che puo` essere generato
 *  \return	Valore massimo
 */
long
DeviceEntropyGenerator::max( void )
{
  return numeric_limits< entropy_t >::max( );
}

/*!
 *  \brief      Ritorna un intero pseudo-random nel range [0; 2**31 - 1]
 *  \return	Entropia generata
 */
long
DeviceEntropyGenerator::gather( void )
{
  entropy_t entropy = -1;
  
  if( 0 != this->source_ )
  {
    while( entropy < 0 ) {
      this->source_->read( reinterpret_cast< char * >(&entropy), sizeof( entropy ) );
    }
    
    return entropy;
  }
  
  // fallback to parent method  
  return TimeEntropyGenerator::gather( );
}
